<template>
  <div class="bigemap" :style="{height:props.height}">
    <div ref="mapContainer" :id="elId"></div>
    <slot></slot>
  </div>
</template>

<script setup lang="ts">
import usePubSub from './usePubSub';
import {useInstanceId} from './useInstanceId';
import { computeAlarmColor, computeAlarmTitle, convertStringToLatLng, convertStringToPath } from '@/utils/gis';
import getScriptAsync from '@/views/dataCenter/bigemap/utils/getScriptAsync';
import boundaryData from '@/views/dataCenter/bigemap/utils/boundary.json';
import {isProd,useBigeMapLayers,map_set_bigemap} from '@/consts.ts';
import {convertLnglatIn, convertLnglatOut} from '@/components/BdGisInput/bigemap';

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const mapContainer = ref<any>();
const emit = defineEmits(['initd','zoomend', 'click', 'mousemove']);
const { emit:emitInject } = usePubSub();
const instanceId = useInstanceId();
const instance = getCurrentInstance();
const elId = computed(() => {
  return `map_${instanceId}`
});

const props = defineProps({
  center: {
    type: Object,
    default: { lng: 128, lat: 45 },
  },
  zoom: {
    type: Number,
    default: 13,
  },
  height: {
    type: String,
    default: '100%',
  },
});

watch(() => props.zoom, () => {
  setTimeout(() => {
    const map = mapStore();
    map?.centerAndZoom(props.center, props.zoom)
  }, 333)
});
watch(() => props.center, () => {
  setTimeout(() => {
    const map = mapStore();
    map?.centerAndZoom(props.center, props.zoom)
  }, 333)
});

const mapStore = () => {
  return (document.querySelector(`#${elId.value}`) as any)?.bmapInst;
};

const initMap = () => {
  getScriptAsync({ exportGetter: () => window.BM }).then(async () => {
    const BM = window.BM;
    BM.Config.HTTP_URL = import.meta.env.VITE_APP_BM_Config_HTTP_URL;
    let BMapGL = null;

    if (!window.BM_BMapGL) {

    BMapGL = {
      convertLnglatIn, convertLnglatOut,
      Autocomplete: function() {
        this.addEventListener = () => {};
      },
      Point: function(lng:any,lat:any) {
        this.lng = +lng;
        this.lat = +lat;
      },
      Marker: function(pt:any,opts:any) {
        const ret = BM.marker(pt, opts);
        ret.setLabel = (label:any) => {
          label.setLatLng(ret._latlng);
          ret.__labelLayer = label;
        };
        return ret;
      },
      Size: function(w,h) {
        this.width = w;
        this.height = h;
      },
      Icon: function(imgUrl:string, size:any, opts:any) {
        const _el = document.createElement('div');
        _el.innerHTML = `<img width="${opts?.imageSize?.width||'auto'}" height="${opts?.imageSize?.height||'auto'}" src="${imgUrl}" />`;
        _el.style.background = 'transparent';
        _el.style.transform = `translate(-${opts?.anchor?.width||0}px, -${opts?.anchor?.height||0}px)`;
        return BM.divIcon({
          html: _el.outerHTML,
        })
      },
      Label: function(content:string, opts:any) {
        const _el = document.createElement('div');
        _el.innerHTML = content;
        _el.style.transform = `translate(${opts?.offset?.x||0}px, ${opts?.offset?.y||0}px)`;
        const position = Object.assign({lat:0,lng:0}, opts.position);
        const ret = BM.marker(position, {
          icon: BM.divIcon({
            html: _el.outerHTML,
            bgPos: [opts?.offset?.x||0, opts?.offset?.y||0],
          }),
          _el,
        });

        ret.setStyle = (styles:any) => {
          Object.keys(styles).forEach((k) => {
            ret.options._el.style[k] = styles[k];
          });
          ret.setIcon(BM.divIcon({
            html: ret.options._el.outerHTML,
          }));
        };
        return ret;
      },
      Polyline: ((base:any) => {
        const Polyline = function(ptList:any[]=[], opts:any={}) {
          const args = [ptList, {
            ...opts,
            color: opts.strokeColor,
            weight: opts.strokeWeight,
            opacity: opts.strokeOpacity,
          }];
          base.apply(this, args);

          Object.defineProperty(this, 'points', {
            get() { return this.getPath(); },
          });
          Object.defineProperty(this, '_config', {
            get() { return this.options; },
          });
          Object.defineProperty(this, 'overlayStr', {
            get() { return 'Polyline'; },
          });
        };

        const prototype = Object.create(base.prototype);
        prototype.constructor = Polyline;
        const {getLatLngs,} = prototype;
        Polyline.prototype = Object.assign(prototype, {
          getPath(convert:boolean=false) {
            const ret = getLatLngs.call(this);
            if (convert) {
              return convertLnglatOut(ret || [])
            } else {
              return ret || []
            }
          },
          setPath(ptList:any[]) {
            this.setLatLngs(ptList);
          },
          setStrokeColor(color:any) {
            this.setOptions({color})
          },
          setStrokeWeight(weight:any) {
            this.setOptions({weight})
          },
          setPositionAt(inx:number,pt:any) {
            const ptList = this.points;
            ptList.splice(inx, 1, pt);
            this.setLatLngs(ptList);
          },
          setOptions(opts:any={}) {
            this.setStyle({
              ...opts,
              color: opts.strokeColor,
              weight: opts.strokeWeight,
              opacity: opts.strokeOpacity,
            });
          },
          enableEditing() {
            console.log('------- enableEditing', this.editing)
            this.editing.enable();
          },
          disableEditing() {
            this.editing.disable();
          },
        });
        return Polyline;
      })(BM.Polyline),
      Polygon: ((base:any) => {
        const Polygon = function(ptList:any[]=[], opts:any={}) {
          const args = [ptList, {
            ...opts,
            color: opts.strokeColor,
            weight: opts.strokeWeight,
            opacity: opts.strokeOpacity,
          }];
          base.apply(this, args);

          Object.defineProperty(this, 'points', {
            get() { return this.getPath(); },
          });
          Object.defineProperty(this, '_config', {
            get() { return this.options; },
          });
          Object.defineProperty(this, 'overlayStr', {
            get() { return 'Polygon'; },
          });
        };

        const prototype = Object.create(base.prototype);
        prototype.constructor = Polygon;
        const {getLatLngs,} = prototype;
        Polygon.prototype = Object.assign(prototype, {
          getPath(convert:boolean=false) {
            const ret = getLatLngs.call(this);
            if (convert) {
              return convertLnglatOut(ret[0] || [])
            } else {
              return ret[0] || []
            }
          },
          setPath(ptList:any[]) {
            this.setLatLngs(ptList);
          },
          setStrokeColor(color:any) {
            this.setOptions({color})
          },
          setStrokeWeight(weight:any) {
            this.setOptions({weight})
          },
          setFillColor(fillColor:any) {
            this.setOptions({fillColor})
          },
          setPositionAt(inx:number,pt:any) {
            const ptList = this.points;
            ptList.splice(inx, 1, pt);
            this.setLatLngs(ptList);
          },
          setOptions(opts:any={}) {
            this.setStyle({
              ...opts,
              color: opts.strokeColor,
              weight: opts.strokeWeight,
              opacity: opts.strokeOpacity,
            });
          },
          enableEditing() {
            this.editing.enable();
          },
          disableEditing() {
            this.editing.disable();
          },
        });
        return Polygon;
      })(BM.Polygon),
      Overlay: ((base:any) => {
        const Overlay = function() {
          const args = Array.prototype.map.call(arguments, (x:any) => x);
          base.apply(this, args);
        };
        const prototype = Object.create(base.prototype);
        prototype.constructor = Overlay;
        Overlay.prototype = Object.assign(prototype, {
          onAdd(mm:any) {
            this._map = mm;
          },
          onRemove(mm) {},
          show() {
            const el = this.container||this.div;
            if (el) { el.style.display = 'block'; }
          },
          hide() {
            const el = this.container||this.div;
            if (el) { el.style.display = 'none'; }
          },
          setStyle(styles:any={}) {
            const el = this.container||this.div;
            if (el) {
              Object.keys(styles).forEach((k) => {
                el.style[k] = styles[k];
              });
            }
          },
          mapSetPosition(pt:any) {
            if (this.div) {
              if (!this._map) {
                this._map = this.map;
              }
              this.bindPopup(this.div, {
                className: 'operateWindow-popup',
                closeButton: false,
              });
              this.openPopup(pt);
            }
          },
        });
        return Overlay;
      })(BM.Layer),
      Control: function() {},
      Symbol: ((base:any) => {
        const Symbol = function() {
          const args = Array.prototype.map.call(arguments, (x:any) => x);
          base.apply(this, args);
          // console.log('------ Symbol', args)
        };

        const prototype = Object.create(base.prototype);
        prototype.constructor = Symbol;
        Symbol.prototype = Object.assign(prototype, {
          setCenter(pt:any) {
            this.setLatLng(pt)
          },
        });
        return Symbol;
      })(BM.CircleMarker),
      Circle: void 0,
      Map: ((base:any) => {
        const Map = function() {
          const args = Array.prototype.map.call(arguments, (x:any) => x);
          base.apply(this, args);

          // this.createPane('floatPane1', this._container);
          // this.createPane('floatPane1');
          this.floatPane = ((mm:any) => {
            const el = document.createElement('div');
            el.classList.add('bigemap-float-pane');
            mm._container.appendChild(el);
            return el;
          })(this);
          this.cusOverlays = [];
          this.labelGroup = BM.layerGroup([]).addTo(this);
          this.drawGroup = BM.layerGroup([]).addTo(this);
          this.drawingGroup = BM.layerGroup([], {pane: 'floatPane'}).addTo(this);
          // this.drawingGroup.options.pane = 'floatPane1';
        };

        const prototype = Object.create(base.prototype);
        prototype.constructor = Map;
        const {getSize,flyTo} = prototype;

        Map.prototype = Object.assign(prototype, {
          enableScrollWheelZoom() {},
          getZoomUnits() {
            return 256*Math.pow(2, this.getZoom())
          },
          addEventListener(eventName:string, cb:any) {
            this.on(eventName, (e:any) => {
              typeof cb =='function' && cb(e);
            });
          },
          getOverlays() {
            return this.cusOverlays;
          },
          clearOverlays() {
            this.cusOverlays.forEach((layer:any) => {
              layer.remove();
            });
            this.cusOverlays = [];
          },
          removeOverlay(layer) {
            if (layer) {
              layer.remove()
            }
            const inx = this.cusOverlays.find((x:any) => x==layer);
            if (inx>-1) {
              this.cusOverlays.splice(inx,1);
            }
          },
          addOverlay(layer:any, container?:any) {
            if (layer) {
              if (container) {
                // layer.options.pane = container.options.pane;
                layer.addTo(container);
              } else {
                layer.addTo(this);
              }
              this.cusOverlays.push(layer);

              if (layer.__labelLayer) {
                layer.__labelLayer.addTo(this);
                this.cusOverlays.push(layer.__labelLayer);
              }
            }
          },
          flyTo(pt:any, zoom:number, convert:boolean=false) {
            if (convert) {
              if (pt) {
                const {lng,lat} = pt;
                flyTo.call(this, convertLnglatIn({lng, lat}), zoom);
              }
            } else {
              flyTo.call(this, pt, zoom);
            }
          },
          centerAndZoom(pt:any,zoom:number, convert:boolean=false) {
            this.flyTo(pt, zoom, convert);
          },
          setCenter(pt:any) {
            this.flyTo(pt);
          },
          setMapStyleV2(opt:any) {
          },
          setViewport(ptList:any[], convert:boolean=false) {
            const bounds = ptList.reduce((ret, cur, inx) => {
              ret[0] = Math.min(ret[0], cur.lng);
              ret[1] = Math.min(ret[1], cur.lat);
              ret[2] = Math.max(ret[2], cur.lng);
              ret[3] = Math.max(ret[3], cur.lat);
              return ret;
            }, [180,90,0,0]);
            const sw = convert ? convertLnglatIn(BM.latLng(bounds[1],bounds[0])) : BM.latLng(bounds[1],bounds[0]);
            const ne = convert ? convertLnglatIn(BM.latLng(bounds[3],bounds[2])) : BM.latLng(bounds[3],bounds[2]);
            this.flyToBounds(BM.latLngBounds(
              sw, ne));
          },
          getSize() {
            const ret = getSize.call(this);
            Object.assign(ret, {width:ret.x,height:ret.y});
            return ret
          },
          pixelToPoint(pixel) {
            const {x,y} = pixel;
            const {_southWest,_northEast} = this.getBounds();
            const {width,height} = this.getSize();
            const ret = {
              lat: height>0 ? _southWest.lat+(_northEast.lat-_southWest.lat)*(1-y/height) : 0,
              lng: width>0 ? _southWest.lng+(_northEast.lng-_southWest.lng)*(x/width) : 0,
            };
            return ret;
          },
          pointToPixel(pt:any) {
            const {lat,lng} = pt;
            const {_southWest,_northEast} = this.getBounds();
            const {width,height} = this.getSize();
            const ret = {
              x: _northEast.lat!=_southWest.lat ? (1-(lat-_southWest.lat)/(_northEast.lat-_southWest.lat))*height : 0,
              y: _northEast.lng!=_southWest.lng ? (lng-_southWest.lng)/(_northEast.lng-_southWest.lng)*width : 0,
            };
            return ret
          },
          pointToOverlayPixel(pt:any) {
            // return this.pointToPixel(pt)
            const {width,height} = this.getSize();
            const {x,y} = this.pointToPixel(pt);
            return {
              x: Math.max(0, Math.min(width, x)),
              y: Math.max(0, Math.min(height, x)),
            }
          },
        });
        return Map;
      })(BM.Map),
      Pixel: ((proto) => {
        const Pixel = function(x,y) {
          this.x = x;
          this.y = y;
        };
        proto.constructor = Pixel;
        Pixel.prototype = Object.create(proto);
        return Pixel;
      })({
        equals(inst) {
          return this.x==inst.x && this.y==inst.y
        }
      }),
      // map: function(container:any, x:any, opts:any) {
      //   return new this.Map(container, x, opts);
      // },
    };
    Object.assign(BMapGL, {
      Circle: ((base:any) => {
        const Circle = function() {
          const args = Array.prototype.map.call(arguments, (x:any) => x);
          base.apply(this, args);
          // console.log('------ Circle', args)
        };

        const prototype = Object.create(base.prototype);
        prototype.constructor = Circle;
        Circle.prototype = Object.assign(prototype, {});
        return Circle;
      })(BMapGL.Symbol)
    });

    window.BM_BMapGL = BMapGL;
    } else {
      BMapGL = window.BM_BMapGL;
    }

    const el:any = document.querySelector(`#${elId.value}`);
    // map = BM.map(mapContainer.value, null, {
    // map = BMapGL.map(mapContainer.value, null, {
    const map = new BMapGL.Map(el, null, {
      center: mapCenter,
      zoom: +await map_set_bigemap.value.__getValue('zoom'),
      minZoom: +await map_set_bigemap.value.__getValue('minZoom'),
      maxZoom: +await map_set_bigemap.value.__getValue('maxZoom'),
      zoomControl: false,
      doubleClickZoom: false,
      // crs: window.BM?.CRS?.Baidu,
    });
    map.setMaxBounds(JSON.parse(await map_set_bigemap.value.__getValue('maxBounds') || '[]'));
    el.bmapInst = map;

    JSON.parse(await map_set_bigemap.value.__getValue('tileLayer') || '[]').forEach((conf:any) => {
      const layer = BM.tileLayer(conf.id).addTo(map);
      if (conf.maxZoom) {
        setTimeout(() => {
          Object.assign(layer.options, {
            maxNativeZoom: +conf.maxNativeZoom,
            maxZoom: +conf.maxZoom,
          })
        }, 333);
      }
    });

    BM.polyline(convertStringToPath(await map_set_bigemap.value.__getValue('boundary') || ''), { color: 'blue', weight: 2 }).addTo(map); // 边界

    map.on('zoomend', () => {
      emit('zoomend', {
        target: {zoomLevel: map.getZoom(),},
      });
    });
    map.on('click', (e:any) => {
      emit('click', {
        latlng: e.latlng,
      });
    });
    map.on('mousemove', (e:any) => {
      emit('mousemove', {
        latlng: e.latlng,
      });
    });


    setTimeout(() => {
      emitInject(instanceId, {
        map,
        instance,
        BM,
      });
  
      emit('initd', {
        map, 
        instance,
        BMapGL,
      });
    }, 133);
  });
};

let mapCenter = { lng: 118.184217, lat: 39.64003 }; // 地图中心
// 获取地图中心点
const getMapCenter = async () => {
  // const centerRes: any = await proxy?.getConfigKey('gis.map.center');
  const centerRes: any = {
    data: await map_set_bigemap.value.__getValue('center'),
  };
  if (centerRes.data) {
    mapCenter = convertStringToLatLng(centerRes.data);
  }
};

onMounted(async () => {
  await getMapCenter();

  nextTick(() => {
    initMap();
  });
});

provide('getMapInstance', () => mapStore());

defineExpose({
  resize() {

  },
})
</script>

<style lang="scss">
body {
  --map-color-green: #2bb367;
  --map-color-blue: #409EFF;
}
.bigemap .bigemap-container {
  height: 100%;
}
.bigemap .bigemap-float-pane {
  position: absolute;
  left: 0;
  top: 0;
}
.bigemap .bigemap-float-pane>div {
  display: none;
  position: absolute;
  left: 0;
  top: 0;
}
.bigemap .bigemap-div-icon {
  width: auto!important;
  height: auto!important;
  margin-left: 0!important;
  margin-top: 0!important;
  background: none!important;
  border: none!important;
  pointer-events: none!important;
}
.bigemap .bigemap-div-icon>div {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #fff;
  white-space: nowrap;
  pointer-events: auto;
}
.bigemap .bigemap-div-icon>div svg {
  position: static;
}

.bigemap .operateWindow {
  display: none;
}
.bigemap .operateWindow-popup
{
  &>.bigemap-popup-content-wrapper {
    background: transparent;
  }
  &>.bigemap-popup-tip-container {
    display: none;
  }
}
</style>